/**
 * @copyright (c) 2024, MacRsh
 *
 * @license SPDX-License-Identifier: Apache-2.0
 *
 * @date 2024-08-03    MacRsh       First version
 */

#include <vec.h>

static int v_resize(Vec_t *vec, size_t capacity) {
    vec->data = realloc(vec->data, capacity * sizeof(void *));
    if (!vec->data) {
        return -ENOMEM;
    }
    vec->capacity = capacity;
    return 0;
}

/**
 * @brief This function initialize a vector.
 *
 * @param vec The vector to be initialized.
 * @param capacity The initial size of the vector.
 * @return 0 on success, otherwise an error code.
 */
int vec_init(Vec_t *vec, size_t capacity) {
    if ((!vec) || (capacity == 0)) {
        return -EINVAL;
    }

    /* Initialize vector */
    vec->data = calloc(capacity, sizeof(void *));
    if (!vec->data) {
        return -ENOMEM;
    }
    vec->capacity = capacity;
    vec->count = 0;
    return 0;
}

/**
 * @brief This function deinitializes a vector.
 *
 * @param vec The vector to be deinitialized.
 */
void vec_deinit(Vec_t *vec) {
    if (!vec) {
        return;
    }

    /* Deinitialize vector */
    vec_clear(vec);
    free(vec->data);
    vec->data = NULL;
    vec->capacity = 0;
}

/**
 * @brief This function creates a new vector.
 *
 * @param capacity The initial size of the vector.
 * @return The created vector on success, otherwise NULL.
 */
Vec_t *vec_new(size_t capacity) {
    Vec_t *vec;

    /* Create vector */
    vec = malloc(sizeof(Vec_t));
    if (!vec) {
        return NULL;
    }
    if (vec_init(vec, capacity)) {
        free(vec);
        return NULL;
    }
    return vec;
}

/**
 * @brief This function destroys a vector.
 *
 * @param vec The vector to be destroyed.
 */
void vec_del(Vec_t *vec) {
    if (!vec) {
        return;
    }

    /* Destroy vector */
    vec_deinit(vec);
    free(vec);
}

/**
 * @brief This function pushes data to the end of the vector.
 *
 * @param vec The vector to push data to.
 * @param data The data to push.
 * @return 0 on success, otherwise an error code.
 */
int vec_push(Vec_t *vec, void *data) {
    if ((!vec) || (!data)) {
        return -EINVAL;
    }

    /* Resize vector if not enough space */
    if (vec->count >= vec->capacity) {
        if (v_resize(vec, vec->capacity * 2)) {
            return -ENOMEM;
        }
    }

    /* Push data to vector */
    vec->data[vec->count] = data;
    vec->count++;
    return 0;
}

/**
 * @brief This function pops data from the end of the vector.
 *
 * @param vec The vector to pop data from.
 * @return The popped data on success, otherwise NULL.
 */
void *vec_pop(Vec_t *vec) {
    if (!vec) {
        return NULL;
    }

    /* Pop data from vector */
    if (vec->count > 0) {
        vec->count--;
    }
    return vec->data[vec->count];
}

/**
 * @brief This function inserts data at the specified index.
 *
 * @param vec The vector to insert data into.
 * @param index The index to insert data at.
 * @param data The data to insert.
 * @return 0 on success, otherwise an error code.
 */
int vec_insert(Vec_t *vec, size_t index, void *data) {
    if ((!vec) || (index > vec->count)) {
        return -EINVAL;
    }

    /* Resize vector if not enough space */
    if (vec->count >= vec->capacity) {
        if (v_resize(vec, vec->capacity * 2)) {
            return -ENOMEM;
        }
    }

    /* Insert data to vector */
    while (index < vec->count) {
        vec->data[index + 1] = vec->data[index];
        index++;
    }
    vec->data[index] = data;
    vec->count++;
    return 0;
}

/**
 * @brief This function removes data at the specified index.
 *
 * @param vec The vector to remove data from.
 * @param index The index to remove data at.
 * @return The removed data on success, otherwise NULL.
 */
void *vec_remove(Vec_t *vec, size_t index) {
    void *data;

    if ((!vec) || (index >= vec->count)) {
        return NULL;
    }

    /* Remove node from vector */
    data = vec->data[index];
    vec->count--;
    while (index < vec->count) {
        vec->data[index] = vec->data[index + 1];
        index++;
    }
    return data;
}

/**
 * @brief This function clears the vector.
 *
 * @param vec The vector to clear.
 */
void vec_clear(Vec_t *vec) {
    if (!vec) {
        return;
    }

    /* Clear vector */
    vec->count = 0;
}

/**
 * @brief This function gets data at the specified index.
 *
 * @param vec The vector to get data from.
 * @param index The index to get data at.
 * @return The data on success, otherwise NULL.
 */
void *vec_get(Vec_t *vec, size_t index) {
    if ((!vec) || (index >= vec->count)) {
        return NULL;
    }

    /* Get data from vector */
    return vec->data[index];
}

/**
 * @brief This function gets the first data in the vector.
 *
 * @param vec The vector to get data from.
 * @return The first data on success, otherwise NULL.
 */
void *vec_first(Vec_t *vec) {
    if (!vec) {
        return NULL;
    }

    /* Get first data from vector */
    return vec->data[0];
}

/**
 * @brief This function gets the last data in the vector.
 *
 * @param vec The vector to get data from.
 * @return The last data on success, otherwise NULL.
 */
void *vec_last(Vec_t *vec) {
    if (!vec) {
        return NULL;
    }

    /* Get last data from vector */
    return vec->data[vec->count - 1];
}

/**
 * @brief This function gets the length of the vector.
 *
 * @param vec The vector to get length from.
 * @return The length of the vector on success, otherwise 0.
 */
size_t vec_len(Vec_t *vec) {
    if (!vec) {
        return 0;
    }

    /* Get length of vector */
    return vec->count;
}

/**
 * @brief This function checks if the vector is empty.
 *
 * @param vec The vector to check.
 * @return True if the vector is empty, otherwise false.
 */
bool vec_is_empty(Vec_t *vec) {
    if (!vec) {
        return true;
    }

    /* Check if vector is empty */
    return (vec->count == 0);
}

/**
 * @brief This function gets the capacity of the vector.
 *
 * @param vec The vector to get capacity from.
 * @return The capacity of the vector on success, otherwise 0.
 */
size_t vec_capacity(Vec_t *vec) {
    if (!vec) {
        return 0;
    }

    /* Get capacity of vector */
    return vec->capacity;
}

/**
 * @brief This function resizes the vector to the specified capacity.
 *
 * @param vec The vector to resize.
 * @param capacity The new capacity of the vector.
 * @return 0 on success, otherwise an error code.
 */
int vec_reserve(Vec_t *vec, size_t reserve_size) {
    size_t capacity;

    if (!vec) {
        return -EINVAL;
    }
    if (reserve_size <= (vec->capacity - vec->count)) {
        return 0;
    }

    /* Resize vector to reserve space */
    capacity = reserve_size + vec->count;
    if (!v_resize(vec, capacity)) {
        return -ENOMEM;
    }
    return 0;
}

/**
 * @brief This function shrinks the vector to the specified capacity.
 *
 * @param vec The vector to shrink.
 * @param capacity The new capacity of the vector.
 * @return 0 on success, otherwise an error code.
 */
int vec_shrink_to(Vec_t *vec, size_t capacity) {
    if ((!vec) || (capacity > vec->capacity) || (capacity < vec->count)) {
        return -EINVAL;
    }
    if (capacity == vec->capacity) {
        return 0;
    }

    /* Shrink vector to new size */
    return v_resize(vec, capacity);
}

/**
 * @brief This function shrinks the vector to the current capacity.
 *
 * @param vec The vector to shrink.
 * @return 0 on success, otherwise an error code.
 */
int vec_shrink_to_fit(Vec_t *vec) {
    if (!vec) {
        return -EINVAL;
    }

    /* Shrink vector to fit current number of elements */
    return v_resize(vec, vec->count);
}
